﻿using System;
using System.Reflection;
using System.Xml;
using Ecopsy.DataTier;
using System.Collections;
using System.IO;
using System.Text;

namespace ClrSqlLibrary
{
    public class ClrSqlEntity :  ReflectionItem,IClrSqlEntity
    {
        public enum XmlConstants {Type,Assembly}
        public ClrSqlEntity() { }

        public virtual void ReadXml(System.Xml.XmlNode Node)
        {
            if (null == Node) return;
            try
            {
                Type MyType = this.GetType();
                PropertyInfo[] MyFields = MyType.GetProperties();                
                foreach (PropertyInfo inf in GetSerisablePropertyList(MyType,"IClrSqlEntity"))
                    {
                    // читаем бизнес объекты                     
                    XmlNode ChildNode = GetNodeByName(Node, inf.Name);
                    if (null == ChildNode) continue;                    
                    
                    object PropValue = inf.GetValue(this, null);
                    if (null == PropValue)
                    {
                        Type Tr = MyGetType(ChildNode.Attributes[SerilizeContext.AssemblyPref].Value, ChildNode.Attributes[SerilizeContext.TypePref].Value);
                        IClrSqlEntity NewObj = (IClrSqlEntity)Activator.CreateInstance(Tr, null);
                        PropValue = NewObj;
                        inf.SetValue(this, PropValue, null);
                        NewObj.ReadXml(ChildNode);
                    }
                    else
                    {
                        ((IClrSqlEntity)PropValue).ReadXml(ChildNode);
                    }
                    
                    }
                
                //читаем списки
                foreach (PropertyInfo inf in GetSerisablePropertyList(MyType, "IList"))
                {
                    // читаем бизнес объекты                     
                    XmlNode ChildNode = GetNodeByName(Node, inf.Name);
                    if (null == ChildNode) continue;
                    IList ListValue = (IList)inf.GetValue(this, null);
                    ListValue.ReadList(ChildNode);

                }

    
                // выставляем значение атрибута
                foreach (PropertyInfo inf in GetSerisableAttProperty(this.GetType()))                
                {
                    XmlAttribute fValue = Node.Attributes[inf.Name];
                    if (fValue == null) continue;
                    if (string.IsNullOrEmpty(fValue.Value)) continue;
                    object val = null;
                    Type t = inf.PropertyType;

                    val = fValue.Value.ToString();

                    if (t.Equals(typeof(Decimal))) val = decimal.Parse(fValue.Value);
                    if (t.Equals(typeof(DateTime))) val = DateTime.Parse(fValue.Value);
                    if (t.Equals(typeof(int))) val = int.Parse(fValue.Value);
                    if (t.Equals(typeof(bool))) val = bool.Parse(fValue.Value);
                    if (inf.CanWrite) inf.SetValue(this, val, null);
                 }
                
            }

            catch (Exception ex)
            {
                throw new Exception
                    (string.Format("Не могу серилизовать объект из ноды {0}", Node.InnerXml), ex);
            }
        }

        public virtual void WriteXml(System.Xml.XmlWriter writer,string TagName)
        {
            try
            {

                Type MyType = this.GetType();
                // начало узла
                writer.WriteStartElement(TagName);
                //// Обязательно пишем тип и сборку 
                writer.WriteAttributeString(XmlConstants.Type.ToString () , MyType.ToString());
                writer.WriteAttributeString(XmlConstants.Assembly.ToString(), MyType.Assembly.ToString());

                Type t = MyGetType(MyType.Assembly.ToString(), MyType.ToString());

                // скалярные атрибуты
                foreach (PropertyInfo inf in GetSerisableAttProperty(this.GetType()))
                {
                    object v = inf.GetValue(this, null);
                    if (null == v) continue;
                    if (String.IsNullOrEmpty(v.ToString())) continue;
                    writer.WriteAttributeString(inf.Name, v.ToString());
                }

                // пишем списки
                foreach (PropertyInfo inf in GetSerisablePropertyList(MyType,"IList"))
                {                    
                    IList  v = (IList)inf.GetValue(this, null);
                    if (null == v) continue;

                    writer.WriteStartElement(inf.Name);
                    foreach (ClrSqlEntity item in v)
                    {
                        
                        item.WriteXml(writer,item.TagName); 
                    }
                    writer.WriteEndElement(); 
                    
                }

                // пишем объекты
                foreach (PropertyInfo inf in GetSerisablePropertyList(MyType, "IClrSqlEntity"))
                {
                    IClrSqlEntity v = (IClrSqlEntity)inf.GetValue(this, null);
                    if (null == v) continue;
                    v.WriteXml(writer,inf.Name); 
                }


             


                writer.WriteEndElement();
            }
            catch
            {
                throw;
            }
        }

        public virtual string TagName
        {
            get { return "ClrSqlEntity"; }
        }        

        private static Type MyGetType(string AssemblyName, string TypeName)
        {
            Assembly a = GetAssembly(AssemblyName);
            if (null == a) return null;

            foreach (Module m in a.GetModules())
            {
                foreach (Type t in m.GetTypes())
                {
                    if (t.ToString().Equals(TypeName)) return t;
                }
            }
            return null;


        }

        private XmlNode GetNodeByName(XmlNode Node, string NodeName)
        {
            try
            {
                foreach (XmlNode item in Node.ChildNodes)
                {
                    if (item.Name.Equals(NodeName)) return item;
                }
                return null;
            }
            catch
            {
                throw;
            }
        }

        private static Assembly GetAssembly(string AssemblyName)
        {
            foreach (Assembly item in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (item.ToString().Equals(AssemblyName)) return item;

            }
            return null;
        }

        public string ToXmlString()
        {
            MemoryStream ms = new MemoryStream();
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb, System.Globalization.CultureInfo.InvariantCulture);
            XmlTextWriter w = new XmlTextWriter(sw);
            this.WriteXml(w, this.TagName);
            return sb.ToString();
        }

    }

}
